global sobel_asm

section .text

; eax, ebx: acumuladores misc
; ecx: contador
; edx: offset
; edi: puntero a la matriz de destino
; esi: puntero a la matriz fuente

; locales
%define bytesporfila [ebp - 4] ; int
%define sumandoextra [ebp - 8] ; int

; parametros
%define columnas     [ebp + 20]
%define filas        [ebp + 16]
%define destino      [ebp + 12]
%define fuente       [ebp + 8]

sobel_asm:
    push    ebp
    mov     ebp, esp
    sub     esp, 8 ; reservo espacio para locales
    push    ebx
    push    edi
    push    esi


; ---------------------------------------------------------------------------------------
; Init: inicia la pila y carga parametros necesarios para el resto del programa. Cuando
; termina, salta a "Sobel" iniciando la iteracion.

init:
    ; arreglo los punteros y contadores teniendo en cuenta que no voy a recorrer toda
    ; la matriz fuente, sino que voy a excluir un borde de 1px en todos lados

    ; almaceno la cantidad de iteraciones reales en ecx
    ; ecx = (filas-2)(columnas-2)
    mov     eax, columnas
    sub     eax, 2
    mov     ebx, filas
    sub     ebx, 2
    mul     ebx
    mov     ecx, eax ; no me preocupo por edx porque impongo una limitacion de 500MB
                     ; al tamanio de la imagen, y bajo esa hipotesis este numero no supera
                     ; los 32 bits.

crearpunteros:
    ; almaceno los punteros
    mov     edi, destino
    mov     esi, fuente


crearbytesporfila:
    ; almaceno la cantidad de bytes que hay en una fila de la imagen 
    ; bytesporfila = 3*columnas
    mov     eax, columnas
    mov     ebx, 3
    mul     ebx
    mov     bytesporfila, eax

crearsumando:
    ; almaceno el sumando extra 
    mov     DWORD sumandoextra, 0
    xor     edx, edx
    mov     ebx, 4
    div     ebx
    cmp     edx, 0
    jz      crearoffset  
    sub     ebx, edx
    mov     sumandoextra, ebx
    add     bytesporfila, ebx  ; actualizo la cantidad de bytes por fila

crearoffset:
    ; almaceno el offset inicial
    mov     edx, bytesporfila
    add     edx, 3

arrancar:
    ; arranco la primera iteracion
    sub     ecx, 1
    jge     sobel       ; salgo si no hay ningun casillero que tratar

terminar:
    jmp     fin


; ---------------------------------------------------------------------------------------
; Sobel: calcula el valor de la convolución de sobel para el elemento actual, y lo
; almacena en la matriz de destino, tras lo cual salta a "Siguiente".

sobel:
    add     edx, 1    ; sumo 1 en edx para usar los pixeles del medio

    ; calculo |Gx| y lo almaceno en ax
    ; para todo esto guardo bytesporfila en ebp (mis disculpas)
    mov     eax, bytesporfila
    push    ebp
    mov     ebp, eax
    sub     esi, ebp  ; achico el puntero para usar mejor el indexado
    sub     edi, ebp
    add     esi, edx  ; agrego el offset
    add     edi, edx
calcularGx:
    ; Gx = |(P2 - P8) * 2 + P1 + P3 - P7 - P9|
    xor     ax, ax
    
    xor     bx, bx

    mov     bl, [esi]         ; P2

    add     ax, bx

    mov     bl, [esi+2*ebp]   ; P8

    sub     ax, bx

    sal     ax, 1             ; *2

    mov     bl, [esi-3]       ; P1

    add     ax, bx

    mov     bl, [esi+3]       ; P3
 
    add     ax, bx

    mov     bl, [esi+2*ebp-3] ; P7
 
    sub     ax, bx

    mov     bl, [esi+2*ebp+3] ; P9
 
    sub     ax, bx

    jnl     calcularGy
    neg     ax

calcularGy:

    ; guardo el resultado en stack para usarlo despues y poder reutilizar el registro
    push    ax

    ; Gy = |(P6 - P4) * 2 + P3 + P9 - P1 - P7|
    xor     ax, ax
    
    mov     bl, [esi+ebp+3]   ; P6

    add     ax, bx

    mov     bl, [esi+ebp-3]   ; P4

    sub     ax, bx

    sal     ax, 1             ; *2

    mov     bl, [esi+3]       ; P3

    add     ax, bx

    mov     bl, [esi+2*ebp+3] ; P9

    add     ax, bx

    mov     bl, [esi-3]       ; P1

    sub     ax, bx

    mov     bl, [esi+2*ebp-3] ; P7

    sub     ax, bx

    jnl     guardar
    neg     ax
 
guardar:
    ; recupero |Gx| y lo sumo a |Gy|
    pop     bx
    add     ax, bx

    ; trunco a 0x00FF si es mayor que ese numero (tiene que caber en un byte!)
    cmp     ax, 0x00FF
    mov     bx, 0x00FF
    cmovnl  ax, bx

listo:
    ; guardo en la matriz de destino 
    mov     [edi+ebp-1], al
    mov     [edi+ebp], al
    mov     [edi+ebp+1], al


finsobel:
    sub     edi, edx
    sub     esi, edx
    add     edi, ebp
    add     esi, ebp
    pop     ebp

    sub     edx, 1

    jmp     siguiente


; ---------------------------------------------------------------------------------------
; Siguiente: prepara el offset para el elemento que sigue, y salta a "Sobel". En caso
; de terminar de contar, salta a "Fin".

siguiente:
    
    cmp     ecx, 0
    jz      fin
  
    ; avanzo 3 bytes (siempre)
    add     edx, 3

    ; examino el offset a ver si llegue al final de una fila, y por
    ; lo tanto corresponde avanzar otros 6 bytes para llegar a la
    ; 2da columna de la fila inferior

    ; guardo el offset en ebx temporariamente para poder usar edx en la division
    mov     ebx,edx

    ; si estoy en el ultimo de una fila avanzo 6 mas
    ; -> if (actual + sumandoextra + 3) mod bytesporfila  == 0
    mov     eax, edx
    add     eax, 3
    add     eax, sumandoextra   ; para la comprobacion tengo que sumar esto, luego lo resto
    mov     edx, 0
    div     DWORD bytesporfila
    sub     eax, sumandoextra
    cmp     edx, 0
    
    ; restauro edx a su valor original
    mov     edx, ebx
    jz      sumar6

finsiguiente:
    dec     ecx
    jmp     sobel

sumar6:
    add     edx, 6
    add     edx, sumandoextra
    jmp     finsiguiente


; ---------------------------------------------------------------------------------------
; Fin: limpia la pila y devuelve.

fin:
    pop     esi
    pop     edi
    pop     ebx
    add     esp, 8
    pop     ebp
    ret
